#ifndef RBS__AST_H
#define RBS__AST_H

#include "rbs/util/rbs_allocator.h"
#include "rbs/util/rbs_constant_pool.h"
#include "string.h"
#include "location.h"

enum rbs_node_type {
<%- nodes.each_with_index do |node, index| -%>
    <%= node.c_type_enum_name %> = <%= index + 1 %>,
<%- end -%>
    RBS_KEYWORD,
    RBS_AST_SYMBOL,
};

typedef struct rbs_node {
    enum rbs_node_type type;
    rbs_location_t *location;
} rbs_node_t;

const char *rbs_node_type_name(rbs_node_t *node);

/* rbs_node_list_node */

typedef struct rbs_node_list_node {
    rbs_node_t *node;
    struct rbs_node_list_node *next;
} rbs_node_list_node_t;

typedef struct rbs_node_list {
    rbs_allocator_t *allocator;
    rbs_node_list_node_t *head;
    rbs_node_list_node_t *tail;
    size_t length;
} rbs_node_list_t;

rbs_node_list_t *rbs_node_list_new(rbs_allocator_t *);

void rbs_node_list_append(rbs_node_list_t *list, rbs_node_t *node);

/* rbs_hash */

typedef struct rbs_hash_node {
    rbs_node_t *key;
    rbs_node_t *value;
    struct rbs_hash_node *next;
} rbs_hash_node_t;

typedef struct rbs_hash {
    rbs_allocator_t *allocator;
    rbs_hash_node_t *head;
    rbs_hash_node_t *tail;
    size_t length;
} rbs_hash_t;

rbs_hash_t *rbs_hash_new(rbs_allocator_t *);

void rbs_hash_set(rbs_hash_t *hash, rbs_node_t *key, rbs_node_t *value);

rbs_hash_node_t *rbs_hash_find(rbs_hash_t *hash, rbs_node_t *key);

rbs_node_t *rbs_hash_get(rbs_hash_t *hash, rbs_node_t *key);

/* rbs_ast_node */

<%- nodes.each do |node| -%>
typedef struct <%= node.c_base_name %> {
    rbs_node_t base;

    <%- node.fields.each do |field| -%>
    <%= field.stored_field_decl %>;
    <%- end -%>
} <%= node.c_type_name %>;

<%- end -%>
typedef union rbs_ast_ruby_annotations {
    rbs_node_t base;
    rbs_ast_ruby_annotations_colon_method_type_annotation_t colon_method_type_annotation;
    rbs_ast_ruby_annotations_method_types_annotation_t method_types_annotation;
    rbs_ast_ruby_annotations_node_type_assertion_t node_type_assertion;
    rbs_ast_ruby_annotations_return_type_annotation_t return_type_annotation;
    rbs_ast_ruby_annotations_skip_annotation_t skip_annotation;
} rbs_ast_ruby_annotations_t;

/// `rbs_keyword_t` models RBS keywords like "private", "instance", "covariant", etc.
/// These are stored in the global constant pool, and get surfaced to Ruby as `Symbol`s,
/// just like `rbs_ast_symbol_t`s.
typedef struct rbs_keyword {
    rbs_node_t base;
    rbs_constant_id_t constant_id;
} rbs_keyword_t;

rbs_keyword_t *rbs_keyword_new(rbs_allocator_t *, rbs_location_t *, rbs_constant_id_t);

/// `rbs_ast_symbol_t` models user-defined identifiers like class names, method names, etc.
/// These get stored in the parser's own constant pool, and get surfaced to Ruby as `Symbol`s.
typedef struct rbs_ast_symbol {
    rbs_node_t base;
    rbs_constant_id_t constant_id;
} rbs_ast_symbol_t;

rbs_ast_symbol_t *rbs_ast_symbol_new(rbs_allocator_t *, rbs_location_t *, rbs_constant_pool_t *, rbs_constant_id_t);

<%- nodes.each do |node| -%>
<%= node.c_type_name %> *<%= node.c_constructor_function_name %>(<%= node.constructor_params.map(&:parameter_decl).join(", ") %>);
<%- end -%>

#endif
